package com.rt.easyjava.common.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rt.easyjava.common.base.BaseBean;
import com.rt.easyjava.common.base.BaseDto;
import com.rt.easyjava.common.base.MyBaseMapper;
import com.rt.easyjava.common.base.Result;
import com.rt.easyjava.common.config.SpringContextBaseHolder;
import com.rt.easyjava.common.config.dozer.MyMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/**
 *  服务实现类
 * @author wuxiaoyong
 * @since 2020-11-15
 */
@Slf4j
public abstract class BaseV2ServiceImpl<M extends MyBaseMapper<B>, B extends BaseBean, D extends BaseDto> extends ServiceImpl<M, B> implements IBaseV2Service<B, D> {
    @Autowired
    protected MyMapper mapper;

    private Class<M> mapperClass;
    private Class<B> beanClass;
    private Class<D> dtoClass;

    public BaseV2ServiceImpl() {
        super();
        Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
        mapperClass = (Class<M>) types[0];
        beanClass = (Class<B>) types[1];
        dtoClass = (Class<D>) types[2];
        log.info("mapperClass:"+mapperClass);
        log.info("beanClass:"+beanClass);
        log.info("dtoClass:"+dtoClass);
    }


    // ==================================================================================
    // ========== 以下使用的是 Mybatis-plus 实现, 适合单表操作 ==============================
    // ==================================================================================

    /**
    * create a object
    * @param dto
    * @return
    */
    @Override
    public Result<D> create(D dto) {
        // dto to entity
        B bean = mapper.map(dto, beanClass);
        bean.setVersion(0L);
        super.baseMapper.insert(bean);

        return Result.builder(bean);
    }


    @Override
    public Result<D> saveBatch(List<D> dtoList) {
        // dto to entity
        List<B> beanList = new ArrayList<>();
        for (D dto:dtoList) {
            B bean = mapper.map(dto, beanClass);
            bean.setVersion(0L);
            beanList.add(bean);
        }
        super.saveBatch(beanList);
        return Result.builder();
    }

    /**
    * update a object by Id
     * 乐观锁：会自动判断和更新version
    * @param dto
    * @return
    */
    @Override
    public Result<D> updateByDtoId(D dto) {
        // dto to entity
        B bean = mapper.map(dto, beanClass);
        B bean2 = super.baseMapper.selectById(dto.getId());
        BeanUtils.copyProperties(bean, bean2, "id","version");
        super.baseMapper.updateById(bean2);

        return Result.builder();
    }

    /**
    * update a object by Id
     * 无乐观锁：不会判断和更新version
    * @param dto
    * @param updateWrapper
    * @return
    */
    @Override
    public Result<D> update(D dto, UpdateWrapper updateWrapper) {
        // dto to entity
        B bean = mapper.map(dto, beanClass);
        super.baseMapper.update(bean, updateWrapper);

        return Result.builder();
    }

    /**
    * 分页 (无合计功能)
    * @param page
    * @param queryWrapper
    * @return
    */
    @Override
    public Result<D> page(Page<B> page, QueryWrapper<B> queryWrapper){
        Result<D> result = new Result<>();

        // mapper
        IPage<B> iPage = super.baseMapper.selectPage(page, queryWrapper);

        // 查询结果转换成DTO
        List<D> dtoList= mapper.maplist(iPage.getRecords(), dtoClass);

        result.getMap().put("total", iPage.getTotal());
        result.getMap().put("rows", dtoList);

        return result.success();
    }

    /**
    * select all by queryWrapper
    * @param queryWrapper
    * @return
    */
    @Override
    public Result<D> selectBy(QueryWrapper<B> queryWrapper) {
        List<B> list = super.baseMapper.selectList(queryWrapper);
        if (list==null) {
            return Result.builder();
        }
        List<D> dtoList = mapper.maplist(list, dtoClass);
        return Result.builder(dtoList);
    }

    /**
    * select one by id
    * @param id
    * @return
    */
    @Override
    public Result<D> selectById(Serializable id) {
        B bean = super.baseMapper.selectById(id);
        if (bean==null) {
            return Result.builder();
        }
        D dto = mapper.map(bean, dtoClass);
        return Result.builder(dto);
    }



    /**
    * select dto list by key
    * @param columnName
    * @param key
    * @return
    */
    @Override
    public Result<D> selectDtoListByKey(String columnName, String key) {
        Result<D> result = new Result<>();

        List<B> beanList = selectListByKey(columnName, key);
        if (beanList == null) {
            return result.fault("no data by key:"+key);
        }
        List<D> dtoList = mapper.maplist(beanList, dtoClass);

        return Result.builder(dtoList);
    }

    /**
    * select bean by key
    * @param columnName
    * @param key
    * @return
    */
    @Override
    public B selectByKey(String columnName, String key) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(columnName, key);
        queryWrapper.eq("deleteflag", 0);
        List<B> beanList = super.baseMapper.selectList(queryWrapper);
        if (beanList.size() == 0) {
            return null;
        }
        return beanList.get(0);
    }

    /**
    * select list bean by key
    * @param columnName
    * @param key
    * @return
    */
    @Override
    public List<B> selectListByKey(String columnName, String key) {
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.eq(columnName, key);
        queryWrapper.eq("deleteflag", 0);
        List<B> beanList = super.baseMapper.selectList(queryWrapper);
        return beanList;
    }






    // ==================================================================================
    // ========== 以下使用的是自定义的 mapper.xml 实现, 适合多表查询 =========================
    // ==================================================================================

    /**
    * 分页查询：mybatis plus 自定义sql分页查询
    * 默认开启合计查询，按需注掉代码
    * @param page
    * @param queryWrapper: 一定要是这个代理工具类才行：QueryWrapper queryWrapper = MyQueryWrapper.buildQueryWrapper(...)
    * @return
    */
    @Override
    public Result<D> myPage(Page<B> page, QueryWrapper<B> queryWrapper) {
        return this.myPage(page, queryWrapper, true);
    }

    @Override
    public Result<D> myPage(Page<B> page, QueryWrapper<B> queryWrapper, Boolean hasFooter) {
        Result<D> result = new Result();

        IPage<B> iPage = super.baseMapper.myPage(page, queryWrapper);
        // 查询结果转换成DTO
        List<D> dtoList = mapper.maplist(iPage.getRecords(), dtoClass);
        // 返回结果集
        result.getMap().put("total", iPage.getTotal());
        result.getMap().put("rows", dtoList);

        // 合计
        if (hasFooter!=null && hasFooter.booleanValue()) {
            List<B> sumList = super.baseMapper.mySelectSum(queryWrapper);
            List<D> sumDtoList = mapper.maplist(sumList, dtoClass);
            result.getMap().put("footer", sumDtoList);
        }

        return result.success();
    }

    /**
    * 查询
    * @param queryWrapper: 一定要是这个代理工具类才行：QueryWrapper queryWrapper = MyQueryWrapper.buildQueryWrapper(...)
    * @return
    */
    @Override
    public Result<D> mySelectBy(QueryWrapper<B> queryWrapper) {
        Result<D> result = new Result();

        List<B> list = super.baseMapper.mySelectBy(queryWrapper);
        List<D> dtoList = mapper.maplist(list, dtoClass);
        result.setList(dtoList);

        return result.success();
    }

    @Override
    public Result<D> mySelectById(Serializable id) {
        Result<D> result = new Result();

        B bean = super.baseMapper.mySelectById(id);
        D dto = mapper.map(bean,dtoClass);
        result.setObj(dto);

        return result.success();
    }


    @Override
    public Result select4column(String columnName){
        try {
            // 取mapperName， eg: tmptAuthUserMapper
            String mapperString = mapperClass.toString();
            String[] mapperStringArr = mapperString.split("\\.");
            String mapperName = mapperStringArr[mapperStringArr.length-1];
            mapperName = mapperName.substring(0,1).toLowerCase()+mapperName.substring(1);

            // vue默认传过来的，出于select option类名考虑，首字母是大写，这样要小字当属性用。
            columnName = columnName.substring(0,1).toLowerCase()+columnName.substring(1);

            Object o = SpringContextBaseHolder.getBean(mapperName);
            Method method = o.getClass().getMethod("mySelect4"+columnName);
            return Result.builder(method.invoke(o));
        }catch (Exception e){
            throw new RuntimeException("方法名不存在");
        }
    }

}
